/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package yoga;

import java.awt.Polygon;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.ArrayList;
import yoga.algo.phenotype.Individual;
import yoga.algo.phenotype.Population;
import yoga.algo.phenotype.PopulationStatistics;
import yoga.algo.phenotype.PopulationStatisticsDataTable;
import yoga.algo.polygon.PolygonFitnessCalc;
import yoga.algo.polygon.PolygonFloatingPointCodec;
import yoga.algo.polygon.PolygonGenerator;
import yoga.algo.process.GARunner;
import yoga.algo.process.MatingScheme;
import yoga.algo.process.RouletteSelection;
import yoga.algo.process.SimpleReproductionMethod;
import yoga.gui.GraphicsUtils;
import yoga.gui.JFramePolygonPairViewer;

/**
 *
 * @author Yogev Montekyo
 */
public class PolygonSmoothing
{
    private GARunner<Polygon> m_gaRunner;
    private JFramePolygonPairViewer m_framePolygonPairViewer;
    private PopulationStatistics<Polygon> m_populationStatistics;
    private PopulationStatisticsDataTable m_populationStatisticsDataTable = new PopulationStatisticsDataTable();

    public void init()
    {
        PolygonFloatingPointCodec polygonCodec = new PolygonFloatingPointCodec();

        //First polygon is KNormal polygon, a true convex with highest circularity value
        Polygon polygonKNormal = PolygonGenerator.getKNormalPolygon(20);
        Polygon initialConvex = PolygonGenerator.getPolygonWithNoise(polygonKNormal, 50);

        PolygonFitnessCalc fitnessCalculator = new PolygonFitnessCalc(initialConvex);
        MatingScheme<Polygon> scheme = new MatingScheme<Polygon>("Polygon 3 Operator scheme",
                new RouletteSelection<Polygon>(),
                fitnessCalculator,
                new SimpleReproductionMethod<Polygon>());

        m_gaRunner = new GARunner<Polygon>(polygonCodec, scheme);

        //Generate initial solution (first generation of polygon population)
        int populationSize = 300;
        int noise = 40;
        ArrayList<Polygon> initialPolygonPopulationList = getInitialPolygonPopulationList(initialConvex, noise, populationSize);
        m_gaRunner.addTypesToPopulation(initialPolygonPopulationList);

        initPolygonViewer(fitnessCalculator, initialConvex);

    }

    private void initPolygonViewer(PolygonFitnessCalc fitnessCalculator, Polygon initialConvex)
    {
        //Init gui and polygon pair viewer
        m_framePolygonPairViewer = new JFramePolygonPairViewer();
        m_framePolygonPairViewer.setTitle("Workshop in Genetic Algorithms");
        m_framePolygonPairViewer.addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });

        m_populationStatistics = new PopulationStatistics<Polygon>(fitnessCalculator);
        m_populationStatistics.updateStatistics(m_gaRunner.getCurrentPopulation());
        String text = getStatisticsAsString(m_populationStatistics, 0);

        m_framePolygonPairViewer.setLeftPolygonPanel(initialConvex, "Initial Polygon");
        m_framePolygonPairViewer.setLeftText(text);

        m_framePolygonPairViewer.setVisible(true);
    }

    public void exec() throws InterruptedException
    {

        boolean showIndividuals = false;
        int maxNumberOfGenerations = 2000;
        m_gaRunner.setMaxNumberOfGenerations(maxNumberOfGenerations);

        System.out.println();
        while (m_gaRunner.nextStep())
        {
            int counter = m_gaRunner.getCurrentGenerationNumber();

            if (counter % m_framePolygonPairViewer.getSliderValue() == 0 || counter == 1)
            {
                updatePolygonViewer(showIndividuals, counter);
            }

        }

        updatePolygonViewer(showIndividuals, maxNumberOfGenerations);
    }

    private void updatePolygonViewer(boolean showIndividuals, int counter) throws InterruptedException
    {
        if (showIndividuals)
        {
            Population<Polygon> population = m_gaRunner.getCurrentPopulation();
            int polygonIndex = 0;
            for (Individual<Polygon> individual : population)
            {
                Polygon polygon = individual.getPhenotype();
                m_framePolygonPairViewer.setRightPolygonPanel(polygon, "#" + polygonIndex);
                m_framePolygonPairViewer.repaint();
                polygonIndex++;
                Thread.sleep(5);
            }
        }
        Individual<Polygon> fittest = m_gaRunner.getFittestIndividual();
        m_framePolygonPairViewer.setRightPolygonPanel(fittest.getPhenotype(), "#" + counter);
        m_populationStatistics.updateStatistics(m_gaRunner.getCurrentPopulation());

        m_populationStatisticsDataTable.addStatistics(m_populationStatistics);

        String text = getStatisticsAsString(m_populationStatistics, counter);


        m_framePolygonPairViewer.setRightText(text);
        m_framePolygonPairViewer.repaint();
        Thread.sleep(10);

    }

    private String getStatisticsAsString(PopulationStatistics<Polygon> populationStatistics, int index)
    {
        StringBuilder text = new StringBuilder();
        text.append("Generation=").append(index).append(" size=").append(populationStatistics.size()).append("\n");
        text.append("Mean=").append(populationStatistics.getMeanFitnessValue()).append("\n");
        text.append("Median=").append(populationStatistics.getMedianFitnessValue()).append("\n");
        text.append("Max=").append(populationStatistics.getMaxFitnessValue()).append("\n");
        text.append("Min=").append(populationStatistics.getMinFitnessValue()).append("\n");

        return text.toString();
    }

    private ArrayList<Polygon> getInitialPolygonPopulationList(Polygon initialPolgon, int noise, int populationSize)
    {
        ArrayList<Polygon> listPolygons = new ArrayList<Polygon>();
        for (int i = 0; i < populationSize; i++)
        {
            Polygon polygon = PolygonGenerator.getPolygonWithNoise(initialPolgon, noise);
            listPolygons.add(polygon);
        }
        return listPolygons;
    }
}
